大家好!在 Day 27,我們成功地為 Firestore 資料庫加上了安全規則,保護了使用者的個人資料隱私。至此,我們後端的安全性已經得到了保障。然而,我們的程式碼中還潛藏著一個明顯的安全風險。
回顧 gemini_service.dart
檔案,我們的 Gemini API 金鑰是這樣寫的:final String _apiKey = 'AIzaSyB...';
將 API Key 這樣的「密鑰 (Secret)」直接寫在程式碼中,被稱為「硬編碼 (Hardcoding)」。這是一個極其危險的壞習慣。如果我們將專案上傳到公開的 GitHub 儲存庫,任何人都能看到我們的金鑰,並盜用我們的 API 額度,可能導致預期外的鉅額帳單。即使是私有儲存庫,這也不是一個專業的管理方式。
今天,我們將學習如何使用 flutter_dotenv
套件,將這個敏感的金鑰從程式碼中抽離,儲存在一個獨立的、被版本控制忽略的檔案中。
flutter pub add flutter_dotenv
建立 .env
檔案:在專案的根目錄(與 pubspec.yaml
同一層),手動建立一個新檔案,並將其命名為 .env
。
儲存你的金鑰:打開 .env 檔案,以 KEY=VALUE 的格式儲存你的金鑰。
# .env
GEMINI_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxx
.env
檔案 (至關重要!):我們必須告訴 Git,永遠不要追蹤這個檔案。打開專案根目錄的 .gitignore
檔案,在檔案的最底部加上新的一行:# .gitignore
# ... 其他忽略規則 ...
# 忽略環境變數檔案
.env
pubspec.yaml
中宣告資源:我們需要讓 Flutter 在打包 App 時,將 .env
檔案包含進去。打開 pubspec.yaml
,在 flutter:
區塊下的 assets:
中加入 .env
檔案的路徑。flutter:
# ...
assets:
- .env # 新增這一行
- assets/icon/
- assets/splash/
.env
檔案本身不會被自動讀取,我們需要在 App 啟動時手動載入它。最佳的時機點就在 main.dart
的 main
函式中。
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // 1. 導入套件
import 'firebase_options.dart';
// ... 其他 import ...
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// 2. 在 Firebase 初始化之前,載入 .env 檔案
await dotenv.load(fileName: ".env");
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
萬事俱備,現在我們可以回到 gemini_service.dart
,移除硬編碼的金鑰,改為從 dotenv
中讀取。
// lib/services/gemini_service.dart
import 'dart:convert';
import 'package:google_generative_ai/google_generative_ai.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // 1. 導入套件
import 'package:snapsaver/models/transction.dart';
class GeminiService {
// 2. 從環境變數中讀取 API 金鑰
final String _apiKey = dotenv.env['GEMINI_API_KEY'] ?? '';
Future<Map<String, dynamic>?> analyzeReceiptText(String ocrText) async {
// 3. 我們在**每個需要呼叫 API 的方法**中,都先檢查 _apiKey 是否為空...
if (_apiKey.isEmpty) {
return {'error': 'Gemini API 金鑰未設定,請檢查您的 .env 檔案。'};
}
// ... 其他程式碼不變,apiKey 參數會自動使用 _apiKey 欄位 ...
}
Future<String?> getExpenseAnalysis(List<Transaction> transactions) async {
if (_apiKey.isEmpty) {
return 'Gemini API 金鑰未設定,請檢查您的 .env 檔案。';
}
// ... 其他程式碼不變 ...
}
}
_apiKey
的值不再是寫死的字串,而是透過 dotenv.env['GEMINI_API_KEY']
從載入的環境變數中讀取。_apiKey
是否為空,如果開發者忘記設定 .env
檔案,App 會回傳一個清晰的錯誤訊息,而不是直接崩潰或發送無效請求。必須完全停止並重新啟動你的 App (Cold Restart),因為 .env
檔案只會在 main
函式執行時被載入一次。Hot Reload 或 Hot Restart 是無效的。
重新啟動後,去測試「智慧掃描」和「智慧分析」功能,它們應該要能像之前一樣正常運作。這代表 App 已經成功地從 .env
檔案中讀取到了你的金鑰。
也可以在終端機執行 git status
,你會發現 .env
檔案完全沒有出現在待提交的清單中,代表它被 .gitignore
成功忽略了!
鐵人賽最終章預告
至此,我們已經完成了所有核心功能的開發、重構、優化與安全加固。我們的 App 無論在功能、架構還是安全性上,都已經達到了一個相當完整的水平。
在鐵人賽的最後兩天,我們將放慢撰寫新程式碼的腳步,轉而進行回顧與總結。
今天我們補上了 App 安全性的最後一塊拼圖。我們學會了:
flutter_dotenv
套件來管理 API 金鑰。.gitignore
以確保敏感檔案不會被納入版本控制。我們的「省錢拍拍」App 現在不僅功能強大、安全可靠,更遵循了專業的開發實踐。明天,讓我們一起來回顧這趟精彩的旅程吧!